
import pygame
# 从pygame模块导入常用的函数和常量
from os import path
#os 包括os.path   import os之后要os.path来调用   
#from os import path后直接用path来调用就可以了
from sys import exit#退出
from time import sleep#时间
from random import choice#选择
from itertools import product#结果
from pygame.locals import QUIT, KEYDOWN#区域变量
# 定义移动和改变的方向
def direction_check(moving_direction, change_direction):
  directions = [['up', 'down'], ['left', 'right']]
#这是一个大列表里套着两个小列表，分为上下和左右，因为蛇正在上下移动，只能改变方向使其左右移动。只改变改变的方向。
  if moving_direction in directions[0] and change_direction in directions[1]:
    return change_direction
  elif moving_direction in directions[1] and change_direction in directions[0]:
    return change_direction
#改变完方向后顺着移动方向前进
  return moving_direction
 
#创建蛇类
class Snake:
#list[1:-1]列表从第二个元素截取到倒数第一个元素（括号里的不知道）
  colors = list(product([70,160], repeat=3))[1:-1]
 
  def __init__(self):
    self.map = {(x, y): 0 for x in range(32) for y in range(24)}#打开的界面共有32*24个点
    self.body = [[100, 100], [120, 100], [140, 100]]
    self.head = [140, 100]
    self.food = []
    self.food_color = []
    self.moving_direction = 'right'
    self.speed = 5
    self.generate_food()#形成新的食物
    self.game_started = False
 #核对游戏状态
  def check_game_status(self):
  #如果蛇的身体或者头部数量大于一，游戏无法运行，游戏界面分辨率是640*480，蛇和食物都是由1个或多个20*20像素的正方形块儿如果超出界面也无法运行
    if self.body.count(self.head) > 1:
      return True
    if self.head[0] < 0 or self.head[0] > 620 or self.head[1] < 0 or self.head[1] > 460:
      return True
    return False
#定义头部移动 
  def move_head(self):
#字典里移动的坐标数据，蛇和食物都是由1个或多个20*20像素的正方形块组成，一次移动一个正方块
    moves = {'right': (20, 0),'up': (0, -20),'down': (0, 20),'left': (-20, 0)}
    step = moves[self.moving_direction]
    self.head[0] += step[0]
    self.head[1] += step[1]
  def generate_food(self):
# 形成食物 
    self.speed = len(self.body) // 16 if len(self.body) // 16 > 5 else self.speed
#如果蛇身长度整除16大于4那么速度改变，若没有保持初始速度
    for seg in self.body:
      x, y =seg
      self.map[x//20, y//20] = 1
# 食物20*20像素的正方形块儿组成
    empty_pos = [pos for pos in self.map.keys() if not self.map[pos]]
    result = choice(empty_pos)
#食物形成在界面空的位置上
    self.food_color = list(choice(self.colors))
# 定义食物颜色
    self.food = [result[0]*20, result[1]*20]
 
 
def main():
  key_direction_dict = {
# 从字典中定义用键盘控制的方向,字典由key和value构成，一个key对应一个value，key-value , 键值对
    119: 'up', # W
    115: 'down', # S
    97: 'left', # A
    100: 'right', # D
    273: 'up', # UP
    274: 'down', # DOWN
    276: 'left', # LEFT
    275: 'right', # RIGHT
  }
 
  fps_clock = pygame.time.Clock()
# 定义一个变量来控制游戏速度 
  pygame.init()
# 初始化所有导入的pygame模块
  pygame.mixer.init()
#初始化混音器模块
  snake = Snake()
  sound = False
  if path.exists('eat.wav'):
    sound_wav = pygame.mixer.Sound("eat.wav")
    sound = True
  title_font = pygame.font.SysFont('arial', 32)
# 基于pygame实现的font游戏字体，使用系统中自带的字体
  welcome_words = title_font.render('Welcome to My Snake', True, (0, 0, 0), (200, 200, 200))
# 'Welcome to My Snake'：显示的内容；True：是否开启抗锯齿，就是说True的话字体会比较平滑，不过相应的速度有一点点影响；(0, 0, 0)：字体颜色；(255, 255, 255))：字体背景颜色
  tips_font = pygame.font.SysFont('arial', 24)
  start_game_words = tips_font.render('Click to Start Game', True, (0, 0, 0), (200, 200, 200))
  close_game_words = tips_font.render('Press ESC to Close', True, (0, 0, 0), (200, 200, 200))
  gameover_words = title_font.render('GAME OVER', True, (205, 92, 92), (200, 200, 200))
  win_words = title_font.render('OH NICE,YOU WIN!', True, (0, 0, 205), (200, 200, 200))
  screen = pygame.display.set_mode((640, 480), 0, 32)
# 初始化一个准备显示的窗口或屏幕
  pygame.display.set_caption('My Snake')
# 设置窗口的标题
  new_direction = snake.moving_direction
  while 1:
    for event in pygame.event.get():#对于pygame.event.get中的事件
      if event.type == QUIT: #event.type事件类型 
#从pygame事件列表中遍历所有事件，如果触发事件的类型 为 pygame的预定事件（如：QUIT），那么执行XXX。
        exit() #退出
      elif event.type == KEYDOWN:
        if event.key == 27:#如果赋值为27
          exit()
        if snake.game_started and event.key in key_direction_dict:
          direction = key_direction_dict[event.key]
          new_direction = direction_check(snake.moving_direction, direction)#新的方向就是蛇运动的方向
      elif (not snake.game_started) and event.type == pygame.MOUSEBUTTONDOWN:
        x, y = pygame.mouse.get_pos()#获取鼠标的位置，返回值为元组类型（x，y）即鼠标位置的坐标 
        if 213 <= x <= 422 and 304 <= y <= 342:#x,y的取值范围
          snake.game_started = True#游戏开始
    screen.fill((200, 200, 200))#屏幕范围
    if snake.game_started:
      snake.moving_direction = new_direction # 在这里赋值，而不是在event事件的循环中赋值，避免按键太快
      snake.move_head()#蛇的头部移动
      snake.body.append(snake.head[:])
      if snake.head == snake.food:
        if sound:
          sound_wav.play()#游戏的音频
        snake.generate_food()#生成食物
      else:
        snake.body.pop(0)#蛇撞击了自己的身体
      for seg in snake.body:
        pygame.draw.rect(screen, [0, 0, 0], [seg[0], seg[1], 20, 20], 0)
      pygame.draw.rect(screen, snake.food_color, [snake.food[0], snake.food[1], 20, 20], 0)
      if snake.check_game_status():
        screen.blit(gameover_words, (241, 310))#结束游戏标志的坐标
        pygame.display.update()#执行这个函数，让我们绘制的东西展示在屏幕上
        snake = Snake()
        new_direction = snake.moving_direction
        sleep(5)#赋值
      elif len(snake.body) == 5:
        screen.blit(win_words, (190, 210))
        pygame.display.update()
        snake = Snake()
        new_direction = snake.moving_direction
        sleep(5)
    else:
      screen.blit(welcome_words, (180, 100))
      screen.blit(start_game_words, (210, 310))
      screen.blit(close_game_words, (210, 350))
      #各个句子在屏幕中所占坐标
    pygame.display.update()#执行这个函数，让我们绘制的东西展示在屏幕上
    fps_clock.tick(snake.speed)#设置游戏的帧率。帧率可以理解为游戏在一秒钟内进行多少次画面刷新。
 
 
if __name__ == '__main__':
  main()
  
  #if __name__ == '__main__' 就相当于是 Python 模拟的程序入口。
  #Python 本身并没有规定这么写，这只是一种编码习惯。由于模块之间相互引用，不同模块可能都有这样的定义，而入口程序只能有一个。
  #到底哪个入口程序被选中，这取决于 __name__ 的值。
